// Copyright (C) 2010 David Sugar, Tycho Softworks.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.

#include <apennine-config.h>
#include <ucommon/ucommon.h>
#include <ucommon/secure.h>
#include <ucommon/export.h>
#include <apennine/namespace.h>
#include <apennine/xmlrpc.h>
#include <new>

namespace apennine {

bool XMLRPC::is_success(void)
{
    if(failed)
        return false;

    return true;
}

int XMLRPC::error(void)
{
    if(!failed)
        return 0;

    return atoi(named("faultCode", NULL));
}

const char *XMLRPC::fault(void)
{
    if(!failed)
        return 0;

    return named("faultString", NULL);
}

const char *XMLRPC::value(const char *type)
{
    if(failed)
        return NULL;

    if(count != 1)
        return NULL;

    if(type && eq(type, "int"))
        type = "i4";

    param *p = (param *)parsed.begin();

    if(!p)
        return NULL;

    if(type && !eq(type, p->type))
        return NULL;

    return p->value;
}

const char *XMLRPC::value(unsigned index, const char *type)
{
    if(!index || index > count || failed)
        return NULL;

    if(type && eq(type, "int"))
        type = "i4";

    linked_pointer<param> p = parsed.begin();
    while(--index && is(p))
        ++p;

    if(is(p)) {
        if(type && !eq(type, p->type))
            return NULL;

        return p->value;
    }
    return NULL;
}

const char *XMLRPC::named(const char *name, const char *type)
{
    if(type && eq(type, "int"))
        type = "i4";

    linked_pointer<param> p = parsed.begin();
    while(is(p)) {
        if(eq(name, p->name)) {
            if(type && !eq(type, p->type))
                return NULL;
            return p->value;
        }
        ++p;
    }
    return NULL;
}

XMLRPC::operator bool()
{
    if(failed)
        return false;

    const char *cp = value("boolean");
    if(!cp)
        return false;

    if(atoi(cp) > 0)
        return true;

    return false;
}

XMLRPC::operator double()
{
    if(failed)
        return 0.;

    const char *cp = value("double");
    if(!cp)
        return 0.;

    return strtod(cp, NULL);
}

XMLRPC::operator rpcint_t()
{
    if(failed)
        return 0;

    const char *cp = value("i4");
    if(!cp)
        cp = value("int");

    if(!cp)
        return 0;

    return atol(cp);
}

XMLRPC::operator const char *()
{
    if(failed)
        return NULL;

    return value("string");
}

const char *XMLRPC::value(unsigned arg, unsigned offset)
{
    unsigned member = 1;

    if(!offset)
        offset = 1;

    linked_pointer<param> p = parsed.begin();
    while(is(p)) {
        if(p->arg > arg)
            break;

        if(p->arg == arg) {
            if(member++ == offset)
                return p->value;
        }

        ++p;
    }
    return NULL;
}

const char *XMLRPC::named(unsigned arg, const char *member)
{
    linked_pointer<param> p = parsed.begin();
    while(is(p)) {
        if(p->arg > arg)
            break;

        if(p->arg == arg && eq(p->name, member))
            return p->value;

        ++p;
    }
    return NULL;
}

const char *XMLRPC::mapped(const char *map, const char *member)
{
    linked_pointer<param> p = parsed.begin();
    while(is(p)) {
        if(eq(p->map, map) && eq(p->name, member))
            return p->value;
        ++p;
    }
    return NULL;
}

XMLRPC::XMLRPC(size_t ps) :
bufpager(ps)
{
    count = argc = 0;
    failed = false;
    transaction = NULL;
    method = NULL;
}

xmlrpc::xmlrpc(size_t ps) :
XMLRPC(ps)
{
}

XMLRPC::param *XMLRPC::create(void)
{
    return new(zalloc(sizeof(param))) param;
}

void XMLRPC::add(param *p)
{
    p->enlist(&parsed);
    ++count;
}

} // end namespace
